package cn.com.nes.site.modules.excel;

import cn.com.nes.common.exception.BusinessException;
import cn.com.nes.common.exception.IllegalBusinessException;
import cn.com.nes.common.util.id.IdUtils;
import cn.com.nes.mybatis.agent.logs.entity.ImportLogDetails;
import cn.com.nes.mybatis.agent.logs.entity.ImportLogs;
import cn.com.nes.site.controller.crm.opportunity.vo.ImportExcelOfEndCustomerVO;
import cn.com.nes.site.modules.excel.dto.EndCustomerDTO;
import cn.com.nes.site.modules.excel.valid.ExcelCheck;
import cn.com.nes.site.modules.sale.SaleService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.fastjson2.JSON;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
public class EndCustomerSyncReadListener extends AnalysisEventListener<EndCustomerDTO> {

    // 结果集
    private final List<EndCustomerDTO> list = new ArrayList<>();

    // 错误结果集
    private final Set<EndCustomerDTO> errorSet = new HashSet<>();

    /**
     * check注解对象
     */
    protected List<Object[]> filedList;

    boolean isEmpty = false;

    @Setter
    private ImportExcelOfEndCustomerVO vo;

    private final List<ImportLogDetails> errorImportLogDetailsList = new ArrayList<>();

    @Setter
    private SaleService saleService;


    private int totol = 0;


    public EndCustomerSyncReadListener(ImportExcelOfEndCustomerVO vo, SaleService saleService) {
        this.vo = vo;
        this.saleService = saleService;
    }

    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        log.error("解析单元格失败,", exception);
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.error("第{}行，第{}列解析异常，数据为:{}", excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
        }
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        super.invokeHead(headMap, context);
    }

    @Override
    public void invoke(EndCustomerDTO data, AnalysisContext context) {
        if (CollectionUtils.isEmpty(list)) {
            Class<?> clazz = data.getClass();
            //含check注解的字段
            filedList = Arrays.stream(clazz.getDeclaredFields())
                    .filter(o -> null != o.getAnnotation(ExcelCheck.class))
                    .map(o -> new Object[]{o, o.getAnnotation(ExcelCheck.class), o.getAnnotation(ExcelProperty.class)})
                    .collect(Collectors.toList());
        }

        list.add(data);
        totol++;

        isEmpty = false;
        // excel格式校验
        if (CollUtil.isNotEmpty(filedList)) {
            checkEmpty(data);
            //存在空值则不进行其他校验
            if (isEmpty) {
                return;
            }
            // 校验长度
            checkLength(data);

            // 业务数据校验
            validateBusiness(data);
        }


    }

    private void validateBusiness(EndCustomerDTO data) {
        validateAreaCode(data);
        validateDataDeviceModel(data);
    }

    private void validateDataDeviceModel(EndCustomerDTO data) {
        try {
            List<String> allowDeviceModels = saleService.getCrmOpportunityService()
                    .getSettingDeviceModels(vo.getMainOpportunityUuid());
            if (CollUtil.isEmpty(allowDeviceModels)) {
                throw new IllegalBusinessException("项目未设置设备型号");
            }
            if (!allowDeviceModels.contains(data.getDeviceModel().substring(0, data.getDeviceModel().indexOf("[")))) {
                ImportLogDetails importLogDetails = new ImportLogDetails();
                importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
                importLogDetails.setXzqhdm(data.getAreaCode().substring(0, data.getAreaCode().indexOf("_")));
                importLogDetails.setOperateUserName(vo.getOperateUserName());
                importLogDetails.setMessage("设备型号不符合项目设置的设备型号");
                importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
                importLogDetails.setType("04");
                importLogDetails.setContactName(data.getContactName());
                importLogDetails.setContactPhone(data.getContactPhone());
                importLogDetails.setIdcardNumber(data.getIdCardNumber());
                importLogDetails.setAddress(data.getAddress());
                errorImportLogDetailsList.add(importLogDetails);
                log.error("设备型号不符合项目设置的设备型号");
                list.remove(data);
                errorSet.add(data);
            }
        } catch (Exception e) {
            log.error("验证设备型号失败：", e);
        }
    }

    private void validateAreaCode(EndCustomerDTO data) {
        try {
            List<String> coverageArea = saleService.getCrmOpportunityService()
                    .getCoverageArea(vo.getMainOpportunityUuid());
            if (CollUtil.isEmpty(coverageArea)) {
                throw new IllegalBusinessException("项目未设置覆盖范围");
            }
            if (!coverageArea.contains(data.getAreaCode().substring(0, data.getAreaCode().indexOf("_")))) {
                ImportLogDetails importLogDetails = new ImportLogDetails();
                importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
                importLogDetails.setXzqhdm(data.getAreaCode().substring(0, data.getAreaCode().indexOf("_")));
                importLogDetails.setOperateUserName(vo.getOperateUserName());
                importLogDetails.setMessage("行政区划不在导入范围内");
                importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
                importLogDetails.setType("04");
                importLogDetails.setContactName(data.getContactName());
                importLogDetails.setContactPhone(data.getContactPhone());
                importLogDetails.setIdcardNumber(data.getIdCardNumber());
                importLogDetails.setAddress(data.getAddress());
                errorImportLogDetailsList.add(importLogDetails);
                log.error("行政区划不在导入范围内");
                list.remove(data);
                errorSet.add(data);
            }
        } catch (Exception e) {
            log.error("验证行政区划失败：", e);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.debug("EndCustomerListener doAfterAllAnalysed 所有数据解析完成后，执行....");
        ImportLogs importLogs = new ImportLogs();
        importLogs.setUuid(IdUtils.getIncreaseIdByNanoTime());
        importLogs.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
        importLogs.setType("04");
        importLogs.setOpportunityUuid(vo.getMainOpportunityUuid());
        importLogs.setProjectName(vo.getOpportunityName());
        importLogs.setOperateUser(vo.getOperateUserName());
        saleService.getImportLogsService().save(importLogs);

        checkRepeat();

        saveData();

        for (ImportLogDetails importLogDetails : errorImportLogDetailsList) {
            importLogDetails.setMainLogUuid(importLogs.getUuid());
        }
        if (CollUtil.isNotEmpty(errorImportLogDetailsList)) {
            saleService.getImportLogDetailsService().saveBatch(errorImportLogDetailsList);
        }

        log.debug("总导入：{}， 成功导入：{}, 导入失败：{}", totol, totol - errorSet.size(), errorSet.size());
        importLogs.setTotalNumber(totol);
        importLogs.setFailNumber(errorSet.size());
        saleService.getImportLogsService().updateById(importLogs);
    }

    private void saveData() {
        Long start = System.currentTimeMillis();
        log.debug("allow import list size: {}", list.size());
        for (EndCustomerDTO endCustomerDTO : list) {
            log.debug("save： {}", endCustomerDTO);
            try {
                saleService.saleSaveHandle(endCustomerDTO.buildSaleBO(vo));
            } catch (Exception e) {
                ImportLogDetails importLogDetails = new ImportLogDetails();
                importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
                importLogDetails.setXzqhdm(endCustomerDTO.getAreaCode()
                        .substring(0, endCustomerDTO.getAreaCode().indexOf("_")));
                importLogDetails.setOperateUserName(vo.getOperateUserName());
                importLogDetails.setMessage(e.getMessage());
                importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
                importLogDetails.setType("04");
                importLogDetails.setContactName(endCustomerDTO.getContactName());
                importLogDetails.setContactPhone(endCustomerDTO.getContactPhone());
                importLogDetails.setIdcardNumber(endCustomerDTO.getIdCardNumber());
                importLogDetails.setAddress(endCustomerDTO.getAddress());
                errorImportLogDetailsList.add(importLogDetails);
                errorSet.add(endCustomerDTO);
                if (e instanceof BusinessException) {
                    log.warn("{}, {}", endCustomerDTO.getContactName(), e.getMessage());
                } else {
                    log.error("", e);
                }
            }
        }

        Long end = System.currentTimeMillis();
        log.debug("总耗时: {}", (end - start) / 1000);
    }


    /**
     * 检验非空
     * @param data
     */
    public void checkEmpty(EndCustomerDTO data) {
        String errorMessage = "";
        for (Object[] os : filedList) {
            Field filed = (Field) os[0];
            filed.setAccessible(true);
            ExcelCheck excelCheck = (ExcelCheck) os[1];
            ExcelProperty excelProperty = (ExcelProperty) os[2];
            try {
                //校验非空
                if (!excelCheck.canEmpty()) {
                    if (filed.get(data) == null || (filed.getType() == String.class && StringUtils.isEmpty((String) filed.get(data)))) {
                        errorMessage = errorMessage + excelProperty.value()[excelProperty.value().length - 1] + "字段为空;";
                        isEmpty = true;
                    }
                }
            } catch (IllegalAccessException e) {
                log.error("校验excel信息失败,", e);
            }
        }
        if (StrUtil.isNotEmpty(errorMessage)) {
            log.error("{}", errorMessage);
            ImportLogDetails importLogDetails = new ImportLogDetails();
            importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
            importLogDetails.setXzqhdm(StrUtil.isEmpty(data.getAreaCode()) ? null : data.getAreaCode()
                    .substring(0, data.getAreaCode().indexOf("_")));
            importLogDetails.setOperateUserName(vo.getOperateUserName());
            importLogDetails.setMessage(errorMessage);
            importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
            importLogDetails.setType("04");
            importLogDetails.setContactName(data.getContactName());
            importLogDetails.setContactPhone(data.getContactPhone());
            importLogDetails.setIdcardNumber(data.getIdCardNumber());
            importLogDetails.setAddress(data.getAddress());
            errorImportLogDetailsList.add(importLogDetails);
            list.remove(data);
            errorSet.add(data);
        }
    }


    /**
     * 校验长度
     * @param data
     */
    public void checkLength(EndCustomerDTO data) {
        String errorMessage = "";
        for (Object[] os : filedList) {
            Field filed = (Field) os[0];
            filed.setAccessible(true);
            ExcelCheck excelCheck = (ExcelCheck) os[1];
            ExcelProperty excelProperty = (ExcelProperty) os[2];
            try {
                //校验非空
                if (excelCheck.length() > 0 && filed.getType() == String.class) {
                    String value = (String) filed.get(data);
                    if (value.indexOf("_") > 0) {
                        value = value.substring(0, value.indexOf("_") - 1);
                    }
                    if (value.length() > excelCheck.length()) {
                        errorMessage = errorMessage + excelProperty.value()[excelProperty.value().length - 1] + "字段长度大于" + excelCheck.length() + ";";
                    }
                }
            } catch (IllegalAccessException e) {
                log.error("校验字段长度失败,", e);
                throw new RuntimeException(e);
            }
        }

        if (StrUtil.isNotEmpty(errorMessage)) {
            log.error("{}", errorMessage);
            ImportLogDetails importLogDetails = new ImportLogDetails();
            importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
            importLogDetails.setXzqhdm(data.getAreaCode().substring(0, data.getAreaCode().indexOf("_")));
            importLogDetails.setOperateUserName(vo.getOperateUserName());
            importLogDetails.setMessage(errorMessage);
            importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
            importLogDetails.setType("04");
            importLogDetails.setContactName(data.getContactName());
            importLogDetails.setContactPhone(data.getContactPhone());
            importLogDetails.setIdcardNumber(data.getIdCardNumber());
            importLogDetails.setAddress(data.getAddress());
            errorImportLogDetailsList.add(importLogDetails);
            list.remove(data);
            errorSet.add(data);
        }
    }


    /**
     * 检验重复
     */
    public void checkRepeat() {
        List<Object[]> repeatAnnotation = filedList.stream().filter(o -> {
            ExcelCheck excelCheck = (ExcelCheck) o[1];
            return !excelCheck.canRepeat();
        }).collect(Collectors.toList());

        for (Object[] objects : repeatAnnotation) {
            ExcelProperty property = (ExcelProperty) objects[2];
            //使用iterate方式构建流以获取行号
            Stream.iterate(0, i -> i + 1).limit(list.size()).collect(Collectors.groupingBy(i -> {
                Field field = (Field) objects[0];
                String result = "";
                try {
                    field.setAccessible(true);
                    result = JSON.toJSONString(field.get(list.get(i)));
                } catch (IllegalAccessException e) {
                    log.error("", e);
                }
                return result;
            }, LinkedHashMap::new, Collectors.mapping(i -> i + 2, Collectors.toList()))).forEach((k, v) -> {
                log.debug("k: {}", k);
                log.debug("v: {}", v);
                if (v.size() > 1) {
                    for (int i = 0; i < v.size(); i++) {
                        if (i == 0) {
                            continue;
                        }
                        log.error("第{}行, {}", v.get(i), property.value()[property.value().length - 1] + "字段重复");
                        // 将重复行号添加到集合中

                        EndCustomerDTO data = list.get(v.get(i) - 2);
                        ImportLogDetails importLogDetails = new ImportLogDetails();
                        importLogDetails.setUuid(IdUtils.getIncreaseIdByNanoTime());
                        importLogDetails.setXzqhdm(data.getAreaCode().substring(0, data.getAreaCode().indexOf("_")));
                        importLogDetails.setOperateUserName(vo.getOperateUserName());
                        importLogDetails.setMessage(property.value()[property.value().length - 1] + "字段重复");
                        importLogDetails.setDrpc(String.valueOf(vo.getOperateTimeStamp()));
                        importLogDetails.setType("04");
                        importLogDetails.setContactName(data.getContactName());
                        importLogDetails.setContactPhone(data.getContactPhone());
                        importLogDetails.setIdcardNumber(data.getIdCardNumber());
                        importLogDetails.setAddress(data.getAddress());
                        errorImportLogDetailsList.add(importLogDetails);
                        list.remove(data);
                        errorSet.add(data);
                    }
                }
            });
        }

    }

}
